package cn.samler.diandi.api.utils;

import cn.samler.diandi.api.config.WechatMiniProgramConfig;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 微信小程序服务端API工具
 *
 * @author samler
 */
@Slf4j
@Component
public class WechatMiniProgramAPIUtil {
    private static WechatMiniProgramAPIUtil ins;
    private final WechatMiniProgramConfig config;
    private static final String API_HOST = "https://api.weixin.qq.com/";
    private final RedisTemplate<String, Object> redisTemplate;

    private WechatMiniProgramAPIUtil(WechatMiniProgramConfig config, RedisTemplate redisTemplate) {
        this.config = config;
        this.redisTemplate = redisTemplate;
    }

    @PostConstruct
    private void init() {
        ins = new WechatMiniProgramAPIUtil(config, redisTemplate);
    }

    public static WechatMiniProgramAPIUtil getInstance() {
        return ins;
    }

    private JSONObject request(String url, List<NameValuePair> params) {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet http;
        CloseableHttpResponse resp;
        try {
            URI uri = new URIBuilder(API_HOST + url).setParameters(params).build();
            http = new HttpGet(uri);
            resp = httpclient.execute(http);

            HttpEntity entity = resp.getEntity();
            if(entity != null)
                return JSON.parseObject(EntityUtils.toString(entity));

            log.warn("request failure: HttpEntity is null");
            return null;
        } catch (Exception e) {
            log.warn("request failure: " + e);

            return null;
        }
    }

    public String getAccessToken() {
        String cacheKey = "wx_mp_ak_" + config.getAppId();
        // 如果缓存有效，则从缓存中获得
        // access_token需要做缓存的原因是微信对获取access_token的频率有限制，需要自行保存，并且有被动刷新的方法
        if(redisTemplate.hasKey(cacheKey)) {
            return (String) redisTemplate.boundValueOps(cacheKey).get();
        }

        List<NameValuePair> params = new ArrayList<>();
        params.add(new BasicNameValuePair("grant_type", "client_credential"));
        params.add(new BasicNameValuePair("appid", config.getAppId()));
        params.add(new BasicNameValuePair("secret", config.getSecret()));
        JSONObject json = request("cgi-bin/token", params);

        if(json != null && json.containsKey("access_token")) {
            String accessToken = json.getString("access_token");
            Long expiresIn = json.getLong("expires_in");
            redisTemplate.boundValueOps(cacheKey).setIfAbsent(accessToken, expiresIn, TimeUnit.SECONDS);

            return accessToken;
        }

        return null;
    }

    public JSONObject getLoginSession(String code) {
        List<NameValuePair> params = new ArrayList<>();
        params.add(new BasicNameValuePair("appid", config.getAppId()));
        params.add(new BasicNameValuePair("secret", config.getSecret()));
        params.add(new BasicNameValuePair("js_code", code));
        params.add(new BasicNameValuePair("grant_type", "authorization_code"));

        return request("sns/jscode2session", params);
    }
}
